package com.objecteffects.template.web.action;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.inject.Inject;

import com.objecteffects.template.tumblr.TumblrMetaResponse;
import com.objecteffects.template.tumblr.TumblrResponse;
import com.objecteffects.template.tumblr.method.Posts;
import com.objecteffects.template.tumblr.method.Posts.PostsResponse.Post;
import com.objecteffects.template.util.logger.InjectLogger;
import com.objecteffects.template.web.utils.BlogPostsOffsets;
import com.objecteffects.template.web.utils.LoginRequired;
import com.objecteffects.template.web.utils.NotLikedPost;
import com.objecteffects.template.web.utils.TumblrAccessToken;

import org.scribe.model.Token;

import org.apache.commons.lang3.StringUtils;

import org.slf4j.Logger;

import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.RedirectResolution;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.SimpleMessage;

/**
 * @author Rusty Wright
 */
@LoginRequired
public class PostsActionBeanBak extends BaseActionBean {
    @InjectLogger
    private Logger log;

    private static final String METHOD = "posts";

    private final BlogPostsOffsets blogPostsOffsets;
    private final TumblrAccessToken tumblrAccessToken;

    private Set<Long> selectedPosts = Collections.emptySet();
    private List<Post> posts;
    private int batchSize;
    private int offset;

    @Inject
    PostsActionBeanBak(final BlogPostsOffsets _blogPostsOffsets,
        final TumblrAccessToken _tumblrAccessToken) {
        this.blogPostsOffsets = _blogPostsOffsets;
        this.tumblrAccessToken = _tumblrAccessToken;
    }

    @DefaultHandler
    public Resolution startHandler() throws IOException {
        final String tumblrUrl = this.getContext().getTumblrUrl();
        final Long userId = this.getContext().getUserId();

        if (tumblrUrl == null) {
            this.log.debug("tumblrUrl is null");

            this.getContext().getMessages()
                .add(new SimpleMessage("Tumblr url is null."));

            return new RedirectResolution(TumblrStartUrlActionBean.class);
        }

        final Token accessToken =
            this.tumblrAccessToken.fetchToken(tumblrUrl, this.getContext()
                                              .getUserId());

        if (accessToken == null) {
            this.log.debug("accessToken is null");

            this.getContext().getMessages()
                .add(new SimpleMessage("Access token is null."));

            return new RedirectResolution(TumblrStartUrlActionBean.class);
        }

        this.batchSize = this.getContext().getBatchSize().intValue();

        if (this.batchSize == 0) {
            this.log.debug("batch size is 0");

            this.getContext().getMessages()
                .add(new SimpleMessage("batch size is 0."));

            return new RedirectResolution(TumblrStartUrlActionBean.class);
        }

        final Integer startingOffset = this.getContext().getStartingOffset();

        if (startingOffset == null) {
            this.log.debug("context starting offset is null");

            this.offset = this.blogPostsOffsets.findOffset(tumblrUrl, userId);

            this.log.debug("offset from db: {}; url: {}, userId: {}",
                           Integer.valueOf(this.offset), tumblrUrl, userId);
        }
        else {
            this.offset = startingOffset.intValue();

            // don't reuse it
            this.getContext().setStartingOffset(null);

            this.log.debug("updating offset: {}", Integer.valueOf(this.offset));

            this.blogPostsOffsets.updateBlogPostsOffsetEntity(tumblrUrl,
                                                              userId,
                                                              this.offset);
        }

        TumblrResponse.Builder<Posts> builder =
            new TumblrResponse.Builder<Posts>().accessToken(accessToken)
                .blog(tumblrUrl).method(PostsActionBeanBak.METHOD)
                .type(Posts.class).limit(Integer.valueOf(this.batchSize));

        builder = builder.offset(Integer.valueOf(this.offset));

        final TumblrResponse<Posts> built = builder.build();

        final Posts response = built.responseMapper();

        if (response == null) {
            this.getContext()
                .getMessages()
                .add(new SimpleMessage(
                         "Permission denied.  Couldn't retrieve posts."));

            return new ForwardResolution(TemplateView.ERROR);
        }

        this.posts = response.getResponse().getPosts();

        if (this.posts.isEmpty()) {
            return new ForwardResolution(TemplateView.POSTS_DONE);
        }

        this.log.debug("posts size: {}", Integer.valueOf(this.posts.size()));

        final List<NotLikedPost> notLikedPosts = this.createNotLikedPosts();

        this.getContext().setNotLikedPosts(notLikedPosts);

        this.log.debug("offset: {}", Integer.valueOf(this.offset));

        return new ForwardResolution(TemplateView.POSTS);
    }

    /**
     * The notlikedPosts is the original list of posts that didn't
     * have the like heart on. The this.posts list is given to the jsp
     * to display; we modify it and remove posts from it that aren't
     * liked.
     *
     * I can't simply point this.posts at notLikedPosts because
     * this.posts is a list of Post and it has the picture url and the
     * other Post stuff.
     *
     * @return
     */
    private List<NotLikedPost> createNotLikedPosts() {
        final List<NotLikedPost> notLikedPosts = new ArrayList<>();

        final Iterator<Post> entries = this.posts.listIterator();

        while (entries.hasNext()) {
            final Post entry = entries.next();

            if (this.removeCriteria(entry)) {
                entries.remove();
            }
            else {
                notLikedPosts.add(new NotLikedPost(entry.getId(), entry
                                                   .getReblogKey()));
            }
        }

        return notLikedPosts;
    }

    /*
     * Remove it from the list of not liked posts if it's liked. Don't
     * remove it if it has a click through url. Don't remove it if it
     * has a caption.
     */
    @SuppressWarnings("static-method")
    private boolean removeCriteria(final Post entry) {
        if (StringUtils.isNotBlank(entry.getLinkUrl())) {
            return false;
        }

        if (StringUtils.isNotBlank(entry.getCaption())) {
            return false;
        }

        if (entry.getLiked() != null) {
            if (entry.getLiked().booleanValue()) {
                return true; // no click through, no caption, liked
            }
        }

        /* no click through url, no caption, but not liked */
        return false;
    }

    public Resolution next() throws IOException {
        final String tumblrUrl = this.getContext().getTumblrUrl();
        final Long userId = this.getContext().getUserId();

        final Token accessToken =
            this.tumblrAccessToken.fetchToken(tumblrUrl, userId);

        int nextOffset = this.blogPostsOffsets.findOffset(tumblrUrl, userId);

        this.batchSize = this.getContext().getBatchSize().intValue();

        this.log.debug("nextOffset: {}, batchSize: {}",
                       Integer.valueOf(nextOffset),
                       Integer.valueOf(this.batchSize));

        if (tumblrUrl == null || accessToken == null || this.batchSize == 0) {
            this.log.debug("tumblrUrl: {}, accessToken: {}, batch size: {}",
                           tumblrUrl, accessToken,
                           Integer.valueOf(this.batchSize));

            this.getContext()
                .getMessages()
                .add(new SimpleMessage(
                         "tumblrUrl or accessToken are null, or batch size is 0."));

            return new RedirectResolution(TumblrStartUrlActionBean.class);
        }

        nextOffset += this.batchSize;

        this.blogPostsOffsets.updateBlogPostsOffsetEntity(tumblrUrl, userId,
                                                          nextOffset);

        final List<NotLikedPost> notLikedPosts =
            this.getContext().getNotLikedPosts();

        if (notLikedPosts == null || notLikedPosts.isEmpty()) {
            this.log.debug("notLikedPosts: {}", notLikedPosts);

            return new RedirectResolution(PostsActionBean.class);
        }

        if (!this.likePosts(tumblrUrl, accessToken, notLikedPosts)) {
            this.getContext()
            .getMessages()
            .add(new SimpleMessage(
                                   "Permission denied. There's a limit of 1000 for how many likes you can do per day."));

            return new ForwardResolution(TemplateView.ERROR);
        }

        return new RedirectResolution(PostsActionBean.class);
    }

    @SuppressWarnings("static-method")
    public Resolution cancel() {
        return new ForwardResolution(TumblrStartUrlActionBean.class);
    }

    private boolean likePosts(final String blog, final Token accessToken,
        final List<NotLikedPost> notLikedPosts) throws IOException {
        if (notLikedPosts.isEmpty()) {
            this.log.debug("notLikedPosts is empty");

            return true;
        }

        if (this.selectedPosts.isEmpty()) {
            this.log.debug("selectedPosts is empty");

            return true;
        }

        TumblrResponse.Builder<TumblrMetaResponse> builder =
            new TumblrResponse.Builder<TumblrMetaResponse>()
                .accessToken(accessToken).blog(blog).method("user/like")
            .setUser().type(TumblrMetaResponse.class);

        /*
         * Why am I iterating over notLikedPosts when I could be
         * iterating over selectedPosts? Because notLikedPosts has the
         * reblog_key. SelectedPosts only has the post id.
         */
        for (final NotLikedPost post : notLikedPosts) {
            if (!this.selectedPosts.contains(post.getPostId())) {
                this.log.debug("skipping post {}", post.getPostId());

                continue;
            }

            builder = builder.id(post.getPostId());
            builder = builder.reblogKey(post.getReblogKey());

            final TumblrResponse<TumblrMetaResponse> built = builder.build();

            final TumblrMetaResponse response = built.responseMapper();

            if (response == null) {
                this.log.debug("response is null");

                return false;
            }

            this.log.debug("response: {}", response);

            if (response.getMeta().getStatus() != 200) {
                this.log.debug("status: {}, msg: {}",
                               Integer.valueOf(response.getMeta().getStatus()),
                               response.getMeta().getMsg());

                return false;
            }
        }

        return true;
    }

    public Set<Long> getSelectedPosts() {
        return this.selectedPosts;
    }

    public void setSelectedPosts(final Set<Long> _selectedPosts) {
        this.log.debug("selectedPosts: {}", _selectedPosts);

        this.selectedPosts = _selectedPosts;
    }

    public List<Post> getPosts() {
        return this.posts;
    }

    /**
     * @return the offset
     */
    public int getOffset() {
        return this.offset;
    }
}
